在 route 中使用 Pinia store 時,不同的 route guard 常常會需要用到同一個 store,我一開始想要少寫一點程式碼XD,減少重複呼叫建立 store 實例,於是將 useStore
引入 route.js
,在最外層的 scope 呼叫建立 store 實例,但這個方法會報錯。
會報錯的範例:
import { useHotelStore } from "@/stores/hotel.js";
const hotelStore = useHotelStore();
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
//略
{
path: "/rooms",
name: "rooms",
component: () => import("../views/RoomsView.vue"),
async beforeEnter(to, from) {
await hotelStore.getRooms();
},
},
{
path: "/room/:id",
name: "room",
component: () => import("../views/RoomView.vue"),
async beforeEnter(to) {
const roomID = to.params.id;
await hotelStore.getRoom(roomID);
},
},
],
});
接下來會說明報錯的原因和正確的寫法。
為什麼會報錯? 這個和 pinia
實例初始化的時機有關。
所有 stores 都依賴 pinia
這個實例,來共享所有的 store 實例。
在 main.js
透過 createPinia()
建立 pinia
實例後,每次在元件中使用 store,其實都是把 pinia
實例從 app
層 inject 到元件中,所以一般在元件內呼叫使用 store 都可以正常運作,因為元件的 setup()
都會在 app.use(createPinia())
後才執行。
來看看 main.js
,為什麼在 route.js
內不能直接呼叫 pinia 的 store:
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
import "./assets/main.css";
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount("#app");
在引入 router
檔案的時候(import router from "./router";
),就會先執行 route.js
,讀取到 useHotelStore
就會報錯,因為這時候 pinia
還沒有實體化,沒有辦法取到 useHotelStore
。
那什麼時候會踩到坑?
想要在 Vue 元件以外的地方使用 Pinia store 的時候,最常見的情境應該就是在 navigation guard 內使用 store 的時候。
正確作法其實沒有什麼特殊的技巧,就是在每次定義 navigation guard 的 callback 時,都要乖乖呼叫 useStore
建立 store 實例。
正確示範如下:
import { useHotelStore } from "@/stores/hotel.js";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
//略
{
path: "/rooms",
name: "rooms",
component: () => import("../views/RoomsView.vue"),
async beforeEnter(to, from) {
const hotelStore = useHotelStore();
await hotelStore.getRooms();
},
},
{
path: "/room/:id",
name: "room",
component: () => import("../views/RoomView.vue"),
async beforeEnter(to) {
const roomID = to.params.id;
const hotelStore = useHotelStore();
await hotelStore.getRoom(roomID);
},
},
],
});
一開始踩到雷的時候蠻困擾的,還是看 stackoverflow 去解 bug,結果,後來發現官方文件根本就有寫哈哈哈(請見參考資料),只是一開始學習 Pinia 時,還不太懂這個主旨的含意跟情境,所以就先跳過這個篇章了 ╥﹏╥
果然...還是要乖乖看官方文件啊~~